package org.jflame.commons.json.fastjson;

import org.jflame.commons.json.JsonCfg;
import org.jflame.commons.json.JsonException;
import org.jflame.commons.json.Jsons;
import org.jflame.commons.util.ArrayHelper;
import org.jflame.commons.util.CollectionHelper;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.Labels;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.alibaba.fastjson.util.IOUtils;

/**
 * 基于fastjson实现json工具类
 * 
 * @author charles.zhang
 */
public class Fastjsons implements Jsons {

    private SerializerFeature[] serializerFeatures;
    private Feature[] parseFeatures;
    // 独立配置,不影响全局
    private SerializeConfig serializeConfig = new SerializeConfig();
    private ParserConfig parserConfig = new ParserConfig();
    private JsonCfg cfg;

    public Fastjsons() {
        this(new JsonCfg());
    }

    public Fastjsons(JsonCfg cfg) {
        configure(cfg);
    }

    @Override
    public String toJson(Object obj) {
        return toJson(obj, (SerializeFilter) null);
    }

    public String toJson(Object obj, SerializeFilter... filters) {
        try {
            return JSON.toJSONString(obj, serializeConfig, filters, cfg.getDateFormat(), JSON.DEFAULT_GENERATE_FEATURE,
                    serializerFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    /**
     * Java对象序列化为JSON字符串,按属性组过滤.
     * <p>
     * 使用LabelFilter定制属性. 如：对象中@JSONField(label = "xx")注解定制.
     * 
     * @see com.alibaba.fastjson.serializer.LabelFilter
     * @param obj Java对象
     * @param includeLabels label分组名称，只包含指定label的属性
     * @return
     */
    public String toJsonView(Object obj, String... includeLabels) {
        try {
            return toJson(obj, Labels.includes(includeLabels));
        } catch (JSONException e) {
            throw new JsonException(e);
        }

    }

    @Override
    public String toJsonFilter(Object obj, boolean isInclude, String[] properties) {
        if (isInclude) {
            return toJsonIncludeField(obj, properties);
        } else {
            return toJsonExcludeField(obj, properties);
        }
    }

    /**
     * Java对象序列化为JSON字符串,指定要排除的属性.
     * 
     * @param obj Java对象
     * @param excludeFields 要排除的属性
     * @return
     */
    public String toJsonExcludeField(Object obj, String... excludeFields) {
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
        for (String field : excludeFields) {
            filter.getExcludes()
                    .add(field);
        }
        try {
            return toJson(obj, filter);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    /**
     * Java对象序列化为JSON字符串,指定要包含的属性.
     * 
     * @param obj Java对象
     * @param includeFields 要包含的属性
     * @return
     */
    public String toJsonIncludeField(Object obj, String... includeFields) {
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(includeFields);
        try {
            return toJson(obj, filter);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    @Override
    public byte[] toJsonBytes(Object obj) {
        try {
            return JSON.toJSONBytes(obj, serializeConfig, null, cfg.getDateFormat(), JSON.DEFAULT_GENERATE_FEATURE,
                    serializerFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    @Override
    public void writeJSONString(OutputStream os, Object object) throws IOException {
        try {
            JSON.writeJSONString(os, IOUtils.UTF8, object, serializeConfig, null, cfg.getDateFormat(),
                    JSON.DEFAULT_GENERATE_FEATURE, serializerFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    @Override
    public <T> T parseObject(String jsonStr, Class<T> clazz) {
        try {
            return JSON.parseObject(jsonStr, clazz, parserConfig, parseFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    public <T> T parseObject(String jsonStr, TypeReference<T> typeRef) {
        return parseObject(jsonStr, typeRef.getType());
    }

    @Override
    public <T> T parseObject(String jsonStr, Type type) {
        try {
            return JSON.parseObject(jsonStr, type, parserConfig, parseFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    @Override
    public <T> T parseObject(InputStream inStream, Charset charset, Class<T> clazz) throws IOException {
        try {
            return JSON.parseObject(inStream, charset, clazz, parserConfig, parseFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    public <T> T parseObject(InputStream inStream, Charset charset, Type typeRef) throws IOException {
        try {
            return JSON.parseObject(inStream, charset, typeRef, parserConfig, parseFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    @Override
    public <T> List<T> parseList(String jsonStr, Class<T> elementClazz) {
        return parseObject(jsonStr, new TypeReference<List<T>>(elementClazz) {
        });
    }

    @Override
    public <K,V> Map<K,V> parseMap(String jsonStr, Class<K> keyClazz, Class<V> valueClazz) {
        return parseObject(jsonStr, new TypeReference<Map<K,V>>(keyClazz, valueClazz) {
        });
    }

    @Override
    public Object parseNode(String jsonStr) {
        try {
            return JSON.parse(jsonStr, parserConfig, parseFeatures);
        } catch (JSONException e) {
            throw new JsonException(e);
        }
    }

    void setPropertyNamingStrategy(int strategy) {
        switch (strategy) {
            case 1:
                serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
                parserConfig.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
                break;
            case 2:
                serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.PascalCase;
                parserConfig.propertyNamingStrategy = PropertyNamingStrategy.PascalCase;
                break;
            case 3:
                serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
                parserConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
                break;
            case 4:
                serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.KebabCase;
                parserConfig.propertyNamingStrategy = PropertyNamingStrategy.KebabCase;
                break;
            default:
                break;
        }
    }

    public void configure(JsonCfg cfg) {
        this.cfg = cfg;
        List<SerializerFeature> writeFeatures = CollectionHelper.newList(SerializerFeature.IgnoreErrorGetter,
                SerializerFeature.DisableCircularReferenceDetect);
        List<Feature> readFeatures = CollectionHelper.newList(Feature.IgnoreNotMatch, Feature.AllowSingleQuotes,
                Feature.DisableFieldSmartMatch);

        configure(writeFeatures, SerializerFeature.WriteBigDecimalAsPlain, cfg.isBigDecimalAsPlain());
        configure(writeFeatures, SerializerFeature.WriteClassName, cfg.isEnableAutoTypeSupport());
        if (cfg.getBrowserCompatible()) {
            configure(writeFeatures, SerializerFeature.BrowserCompatible, cfg.getBrowserCompatible());
        }
        configure(writeFeatures, SerializerFeature.PrettyFormat, cfg.isPrettyFormat());
        configure(writeFeatures, SerializerFeature.WriteMapNullValue, cfg.isIgnoreNull());

        if (cfg.isEnableAutoTypeSupport()) {
            configure(readFeatures, Feature.SupportAutoType, cfg.isEnableAutoTypeSupport());
        }

        serializerFeatures = ArrayHelper.toArray(writeFeatures);
        parseFeatures = ArrayHelper.toArray(readFeatures);
        if (cfg.getNamingStrategy() != null) {
            setPropertyNamingStrategy(cfg.getNamingStrategy());
        }
        // 时区只有全局配置
        if (cfg.getTimeZone() != null) {
            JSON.defaultTimeZone = cfg.getTimeZone();
        }

    }

    <T> void configure(List<T> cfgArray, T cfg, boolean enable) {
        if (cfgArray.contains(cfg)) {
            if (!enable) {
                cfgArray.remove(cfg);
            }
        } else {
            if (enable) {
                cfgArray.add(cfg);
            }
        }
    }

}
